/*+***********************************************************************************
 Filename: 9k_mcu01_mycore_v01\src\zh_uart_v01.v
 Description: a simple uart peripheral module.
     fix bardrate=115200

 Modification:
   2022.11.08 Creation   H.Zheng (03_tinycore_step05)
   2025.08.17 rearrange to 9k_mcu01_mycore_v01 project
              change addr to 6bit width


Copyright (C) 2022-2025  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/

/**
 * I/O Regs:
 *  offset      reg
 * 0x00 write   tx
 * 0x00 read    rx data  
 * 0x04         rx status: bit0=rx_valid, clear after read rx data
 * 0x08         tx status: bit0=tx_busy
 */

module zh_uart_v01 #(parameter CLK_FREQ_IN_MHz=27)(
    //interface with top
    input wire clk,
    input wire reset_n,
    input wire rxd,
    output wire txd,
    //interface with core bus
    input wire ce, //chip enable
    input wire wre, //write enable
    input wire [5:0] addr, //address bus
    input wire [31:0] data_in,
    output wire [31:0] data_out,
    output wire int_rx_valid_o
);

    reg [31:0] data_tx_reg;
    wire status_tx_busy;

    wire uart_tx_en = ce & wre & (addr==6'h0) &(~status_tx_busy);

    //write data_tx_reg register
    always @(posedge clk) begin
        if (~reset_n) begin
            data_tx_reg <= 32'b0;
        end
        else if (uart_tx_en) begin
            data_tx_reg <= data_in;
        end
    end    

    //generate tx pulse
    reg [1:0] shift_reg;
    always @(posedge clk) begin
        if (~reset_n) begin
            shift_reg <= 2'b0;
        end
        else begin
            shift_reg <= {shift_reg[0], uart_tx_en};
        end
    end    
    wire uart_tx_en_pulse = (shift_reg[1] == 0) & (shift_reg[0] == 1);

    //rx ctrl&status signal
    wire rx_data_en = ce & (~wre) & (addr==6'h0);
    wire status_rx_rd_en = ce & (~wre) & (addr==6'h1);
    wire status_tx_rd_en = ce & (~wre) & (addr==6'h2);
//    reg [7:0] rx_data_reg;
    reg status_rx_valid;

    reg [31:0] data_rx_reg;
    reg rx_data_en_flag_reg;
    always @(posedge clk) begin
      rx_data_en_flag_reg <= rx_data_en;
    end

    always @(negedge clk) begin
        if (~reset_n) begin
            data_rx_reg <= 32'b0;
            status_rx_valid <= 0;
        end
        else if (rx_flag) begin
            data_rx_reg <= {24'b0, rx_data};
            status_rx_valid <= 1;
        end
        else if (rx_data_en_flag_reg) begin
            status_rx_valid <= 0;
        end
    end   
    //
    wire [7:0] rx_data;
    uart_rx #(.BAUDRATE_DIVIDER(CLK_FREQ_IN_MHz*1000000/115200)) u_uart0_rx( 
       .clk(clk),  
       .reset_n(reset_n),
       .rxd(rxd),
       .rx_data(rx_data),
       .rx_flag(rx_flag)
    );

    uart_tx #(.BAUDRATE_DIVIDER(CLK_FREQ_IN_MHz*1000000/115200)) u_uart0_tx(
       .clk(~clk),  
       .reset_n(reset_n),
       .tx_en(uart_tx_en_pulse),
       .tx_data(data_tx_reg[7:0]),
       .txd(txd),
       .tx_busy(status_tx_busy)
    );


    //
    assign data_out = rx_data_en ? data_rx_reg :
                      status_rx_rd_en ? {31'b0, status_rx_valid} : 
                      status_tx_rd_en ? {31'b0, status_tx_busy} : 32'b0;

    //int request
    assign int_rx_valid_o = status_rx_valid;

endmodule
